﻿using Cysharp.Threading.Tasks;
using FixMath.NET;
using GameProtocol.Protocol;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Net;
using TEngine;
using UnityEngine;
using UnityEngine.Events;

namespace GameLogic
{
    class PlayerInfo
    {
        public PlayerInfo(uint conversationID) {
            ConversationID = conversationID;
        } 
        public uint ConversationID { get; set; }
        public int PlayerControlShipID { get; set; }
    }

    public class FightServer : UDPServer
    {

        Dictionary<string, PlayerInfo> m_ClientMap = new Dictionary<string, PlayerInfo>();
        protected Dictionary<long, GameProtocol.FrameSyncData> m_ServerFrameBuffer = new Dictionary<long, GameProtocol.FrameSyncData>();
        public int ClientCount { get { return m_ClientMap.Count; } }

        public static FightServer Create()
        {
            FightServer server = new FightServer();
            server.Init(35665, 35664);
            server.AddListener(GameProtocol.Protocol.CSMsgId.SwitchShipRqr, server.OnSwitchShipRqr);
            server.AddListener(GameProtocol.Protocol.CSMsgId.FrameSyncRqr, server.OnFrameUpdateRequire);
            server.AddListener(GameProtocol.Protocol.CSMsgId.DisconnectRqr, server.OnDisconnectRequire);
            return server;
        }


        #region 公有函数
        public void StartGame()
        {
            Send(GameProtocol.Protocol.CSMsgId.StartGameNtf, new IPEndPoint(IPAddress.Broadcast, m_SendPort), null);
            m_IsStarted = true;
        }
        protected bool m_IsStarted = false;
        protected long m_ServerLogicFrameCount = 0;
        protected Fix64 m_PreviousFrameTimeStamp;  //  上一次纪录帧的时间戳
        public override void Update()
        {
            base.Update();
            if (!m_IsStarted)
                return;
            //bool needPause = m_ServerLogicFrameCount > m_ServerLogicFrameCount + 15;
            while (m_ServerFrameBuffer.ContainsKey(m_ServerLogicFrameCount) &&
                m_ServerFrameBuffer[m_ServerLogicFrameCount].PlayerCount == ClientCount)
            {
                var byteData = m_ServerFrameBuffer[m_ServerLogicFrameCount].ToByteArray();
                Send(GameProtocol.Protocol.CSMsgId.FrameSyncRsp, new IPEndPoint(IPAddress.Broadcast, m_SendPort), byteData);
                m_PreviousFrameTimeStamp = m_PreviousFrameTimeStamp + SystemModule.Instance.LogicDelta;
                m_ServerLogicFrameCount++;
            }
        }

        public override void Disconnect()
        {
            Send(CSMsgId.CloseRoomNtf, new IPEndPoint(IPAddress.Broadcast, m_SendPort), null);
            base.Disconnect();
            m_ClientMap.Clear();
        }

        public override void Clear()
        {
            base.Clear();
        }
        #endregion
        #region 回调
        protected override void OnClientTimeout(ClientHandle clientHand)
        {
            base.OnClientTimeout(clientHand);
            //m_ClientMap.Remove(clientHand.IP);
            //Debug.LogWarning("Player "+ clientHand.IP + "has disconnected!!");
        }

        int m_PosX = 2;
        void OnSwitchShipRqr(IPEndPoint ep, byte[] data)
        {
            var dataD = GameProtocol.Protocol.SwitchShipRqrData.Parser.ParseFrom(data);
            if (dataD != null)
            {
                string ipStr = ep.Address.ToString();
                GameProtocol.ShipData shipData = dataD.ShipData;
                PlayerInfo playerInfo = null;
                if (!m_ClientMap.ContainsKey(ipStr))
                {
                    playerInfo = new PlayerInfo(dataD.PlayerID);
                    playerInfo.PlayerControlShipID = ShipModule.Instance.GenerateShipID();
                    m_ClientMap.Add(ipStr, playerInfo);
                }
                else
                {
                    playerInfo = m_ClientMap[ipStr];
                }
                UnityEngine.Debug.Log("switch ship: " + ipStr);
                shipData.PosX = new Fix64(m_PosX).RawValue;
                shipData.InstanceID = playerInfo.PlayerControlShipID;
                GameProtocol.Protocol.SwitchShipRspData dataRsp = new GameProtocol.Protocol.SwitchShipRspData();
                dataRsp.JoinShip = shipData;
                dataRsp.PlayerID = dataD.PlayerID;

                foreach (var i in ShipModule.Instance.Ships)
                    dataRsp.AllShips.Add(i.Value.ShipData.InstanceID, i.Value.ShipData.Data);

                m_PosX += 2;
                Send(GameProtocol.Protocol.CSMsgId.SwitchShipRsp, new IPEndPoint(IPAddress.Broadcast, m_SendPort), dataRsp.ToByteArray());
            }
        }
        void OnDisconnectRequire(IPEndPoint ip, byte[] data)
        {
            try
            {
                var dataContent = DisconnectRqrData.Parser.ParseFrom(data);

                var disconnectNtfData = new DisconnectNtfData();
                disconnectNtfData.ConversationID = dataContent.ConversationID;
                m_ClientMap.Remove(ip.Address.ToString());
                Send(GameProtocol.Protocol.CSMsgId.DisconnectedNtf, new IPEndPoint(IPAddress.Broadcast, m_SendPort), disconnectNtfData.ToByteArray());
            }
            catch (Exception e)
            {
                Debug.Log(e.ToString());
            }
        }

        object lockObj = new object();
        void OnFrameUpdateRequire(IPEndPoint ip, byte[] data)
        {
            try
            {
                var syncData = GameProtocol.FrameSyncData.Parser.ParseFrom(data);
                var delay = DateTime.UtcNow.Ticks - syncData.TimeStamp;

                if (syncData != null && syncData.LogicFrameID >= 0)
                {
                    Debug.Log("Received frame " + syncData.LogicFrameID +", from ="+ip.Address.ToString());
                    lock (lockObj)
                    {
                        if (!m_ServerFrameBuffer.ContainsKey(syncData.LogicFrameID))
                            m_ServerFrameBuffer.Add(syncData.LogicFrameID, syncData);
                        else
                            m_ServerFrameBuffer[syncData.LogicFrameID].MergeFrom(syncData);
                    }
                }
            }
            catch (Exception e)
            {
                Debug.LogError(e);
            }
        }
        #endregion
    }
}
